﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Extented.UI.Core.Native
{
    public class RenderTextBlock : FrameworkRenderElement
    {
        static readonly RenderRealTextBlock RealTextBlockTemplate = new RenderRealTextBlock();
        TextTrimming textTrimming;
        TextWrapping textWrapping = TextWrapping.NoWrap;
        TextAlignment textAlignment;
        TextDecorationCollection textDecorations;
        public TextTrimming TextTrimming
        {
            get { return textTrimming; }
            set { SetProperty(ref textTrimming, value); }
        }
        public TextWrapping TextWrapping
        {
            get { return textWrapping; }
            set { SetProperty(ref textWrapping, value); }
        }
        public TextAlignment TextAlignment
        {
            get { return textAlignment; }
            set { SetProperty(ref textAlignment, value); }
        }
        public TextDecorationCollection TextDecorations
        {
            get { return textDecorations; }
            set { SetProperty(ref textDecorations, value); }
        }
        public RenderTextBlock()
        {
            VerticalAlignment = VerticalAlignment.Center;
            ContentSpecificClipToBounds = true;
        }
        protected override void PreApplyTemplate(FrameworkRenderElementContext context)
        {
            base.PreApplyTemplate(context);
            var tbContext = (RenderTextBlockContext)context;
            var textWrapping = tbContext.TextWrapping ?? TextWrapping;
            var textTrimming = tbContext.TextTrimming ?? TextTrimming;
            var textAlignment = tbContext.TextAlignment ?? TextAlignment;
            var textDecorations = tbContext.TextDecorations ?? TextDecorations;
            string highlightedText = tbContext.HighlightedText;
            if (ShouldUseRealTextBlock(tbContext, textWrapping, textTrimming, highlightedText))
            {
                var realTBContext = tbContext.Child;
                if (realTBContext == null)
                {
                    realTBContext = (RenderRealTextBlockContext)RealTextBlockTemplate.CreateContext(context.Namescope, context.ElementHost);
                    context.AddChild(realTBContext);
                }
                realTBContext.HighlightedText = highlightedText;
                realTBContext.HighlightedTextCriteria = tbContext.HighlightedTextCriteria;
                realTBContext.Text = tbContext.Text;
                realTBContext.TextWrapping = textWrapping;
                realTBContext.TextTrimming = textTrimming;
                realTBContext.TextAlignment = textAlignment;
                realTBContext.TextDecorations = textDecorations;
                return;
            }
            if (tbContext.Typeface == null)
                tbContext.Typeface = CreateTypeface(tbContext);
            if (tbContext.FormattedText == null)
                tbContext.FormattedText = CreateFormattedText(tbContext);
        }
        public static bool ShouldUseRealTextBlock(RenderTextBlockContext tb, TextWrapping textWrapping, TextTrimming textTrimming, string highlightedText)
        {
            if (tb.ForceUseRealTextBlock)
                return true;
            if (!string.IsNullOrEmpty(highlightedText))
                return true;
            return textWrapping != TextWrapping.NoWrap;
        }
        protected override Size MeasureOverride(Size availableSize, IFrameworkRenderElementContext context)
        {
            var tbContext = (RenderTextBlockContext)context;
            if (tbContext.UseRealTextBlock)
                return LayoutProvider.GridInstance.MeasureOverride(availableSize, context);
            var textTrimming = tbContext.TextTrimming ?? TextTrimming;
            var currentTextDecorations = tbContext.TextDecorations ?? TextDecorations;
            FormattedText formattedText = tbContext.FormattedText;
            formattedText.TextAlignment = GetTextAlignment(tbContext);
            formattedText.Trimming = textTrimming;
            if (!Equals(currentTextDecorations, tbContext.CachedTextDecorations))
            {
                formattedText.SetTextDecorations(currentTextDecorations);
                tbContext.CachedTextDecorations = currentTextDecorations;
            }
            bool useTextTrimming = textTrimming != TextTrimming.None;
            double width = useTextTrimming ? Math.Min(availableSize.Width, Math.Ceiling(tbContext.FormattedText.Width)) : tbContext.FormattedText.Width;
            return new Size(width, tbContext.FormattedText.Height);
        }
        protected override Size ArrangeOverride(Size finalSize, IFrameworkRenderElementContext context)
        {
            var tbContext = (RenderTextBlockContext)context;
            if (tbContext.UseRealTextBlock)
                return LayoutProvider.GridInstance.ArrangeOverride(finalSize, context);
            FormattedText formattedText = tbContext.FormattedText;
            var textTrimming = tbContext.TextTrimming ?? TextTrimming;
            if (textTrimming != TextTrimming.None)
            {
                formattedText.MaxTextWidth = finalSize.Width;
                formattedText.MaxTextHeight = finalSize.Height + 1;
            }
            return base.ArrangeOverride(finalSize, context);
        }
        protected override bool CalcNeedToClipSlot(Size layoutSlotSize, FrameworkRenderElementContext context)
        {
            var tbContext = (RenderTextBlockContext)context;
            var textTrimming = tbContext.TextTrimming ?? TextTrimming;
            if (textTrimming == TextTrimming.None || (!tbContext.NeedsClipBounds && IsTextGreaterThan(tbContext.FormattedText, layoutSlotSize)))
                return true;
            return false;
        }
        bool IsTextGreaterThan(FormattedText formattedText, Size finalSize)
        {
            if (formattedText.Width > finalSize.Width)
                return true;
            if (formattedText.Height > finalSize.Height + 1)
                return true;
            return false;
        }
        protected override void RenderOverride(DrawingContext dc, IFrameworkRenderElementContext context)
        {
            base.RenderOverride(dc, context);
            var tbContext = (RenderTextBlockContext)context;
            if (tbContext.UseRealTextBlock)
                return;
            FormattedText formattedText = tbContext.FormattedText;
            Rect renderRect = context.RenderRect;
            if (renderRect.Width.AreClose(0d) || renderRect.Height.AreClose(0d))
                return;
            TextAlignment textAlignment = GetTextAlignment(tbContext);
            var textTrimming = tbContext.TextTrimming ?? TextTrimming;
            dc.DrawText(formattedText, CalcAnchorPointForDrawText(renderRect, textAlignment, textTrimming, IsRtl(tbContext)));
        }
        bool IsRtl(RenderTextBlockContext tbContext)
        {
            FlowDirection fd = tbContext.FlowDirection ?? FlowDirection ?? default(FlowDirection);
            return fd == System.Windows.FlowDirection.RightToLeft;
        }
        Point CalcAnchorPointForDrawText(Rect renderRect, TextAlignment textAlignment, TextTrimming textTrimming, bool isRtl)
        {
            if (textTrimming != TextTrimming.None)
                return new Point(0, 0);
            bool isNativeLeft = textAlignment == TextAlignment.Left || textAlignment == TextAlignment.Justify;
            bool isLeft = (isNativeLeft && !isRtl) || (textAlignment == TextAlignment.Right && isRtl);
            bool isRight = (textAlignment == TextAlignment.Right && !isRtl) || (isNativeLeft && isRtl);
            if (isLeft)
                return new Point(0, 0);
            if (isRight)
                return new Point(renderRect.Width, 0);
            return new Point(renderRect.Width / 2, 0);
        }
        TextAlignment GetTextAlignment(RenderTextBlockContext context)
        {
            return context.TextAlignment ?? TextAlignment;
        }
        FormattedText CreateFormattedText(RenderTextBlockContext context)
        {
            string value = context.Text;
            FrameworkElement root = context.ElementHost.Parent;
            var fd = context.FlowDirection.HasValue ? context.FlowDirection.Value : System.Windows.FlowDirection.LeftToRight;
            var foreground = context.Foreground ?? TextBlock.GetForeground(root);
            var ft = new FormattedText(string.IsNullOrEmpty(value) ? " " : value, System.Globalization.CultureInfo.CurrentCulture, fd, context.Typeface, TextBlock.GetFontSize(root),
                foreground, new NumberSubstitution() { Substitution = NumberSubstitution.GetSubstitution(root) }, TextOptions.GetTextFormattingMode(root));
            ft.Trimming = TextTrimming;
            return ft;
        }
        Typeface CreateTypeface(RenderTextBlockContext context)
        {
            FrameworkElement root = context.ElementHost.Parent;
            return new Typeface(TextBlock.GetFontFamily(root), TextBlock.GetFontStyle(root), TextBlock.GetFontWeight(root), TextBlock.GetFontStretch(root));
        }
        protected override FrameworkRenderElementContext CreateContextInstance()
        {
            return new RenderTextBlockContext(this);
        }
    }
}
